package Control;

import Modelo.Caballero;
import Vista.Fight_Frame;
import Vista.Main_Frame;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JOptionPane;

public class Controlador extends Observable {

    private static Controlador instancia = null;
    public Caballero jugador1;
    public Caballero jugador2;
    public int dano;
    private Random randomGenerator;
    public XML xmlHandler;
    public int escenarios;
    private int pelea;
    private ArrayList<Caballero> oponentes;
    private boolean habi;
    public Clip sonido;
    //aqui es donde vamos a tener dos Personajes privados para toda la sesion

    public Controlador() {
        randomGenerator = new Random();
        xmlHandler = new XML(getClass().getResource("/Aplicacion/info.xml"));
        
        xmlHandler.leerXml();
        jugador1 = new Caballero();
        jugador2 = new Caballero();
        pelea = 0;
        habi = false;
        try {
            sonido = AudioSystem.getClip();
        } catch (LineUnavailableException ex) {
            Logger.getLogger(Controlador.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static Controlador obtenerInstancia() {
        if (instancia == null) {
            instancia = new Controlador();
        }
        return instancia;
    }

    public Caballero buscarCaballero(String nombre) {
        Iterator<Caballero> it = XML.CabBuenos.iterator();
        while (it.hasNext()) {
            Caballero obj = it.next();
            if (obj.getNombre().equals(nombre)) {
                System.out.println("Encontrado");
                return obj;

            }
        }
        Iterator<Caballero> it2 = XML.CabMalos.iterator();
        while (it2.hasNext()) {
            Caballero obj = it2.next();
            if (obj.getNombre().equals(nombre)) {
                System.out.println("Encontrado");
                return obj;

            }
        }
        System.out.println("NO Encontrado");
        return null;
    }

    public void registrarObservador(Observer nuevoObservador) {

        // Los cambios que sufren los caballeros, se va a reflejar en el observador, en nuestro caso
        // el observador Main_Frame recibira los cambios desde la clase caballero, y mostrara el mensaje emitida por el Caballero
        jugador1.addObserver(nuevoObservador);
        jugador2.addObserver(nuevoObservador);
        this.addObserver(nuevoObservador);
    }

    //Este es el metodo que nosotros vamos a asignar un enemigo dentro de los caballeros MALOS
    // Este metodo hay que MEJORAR, hasta incluso modificarlo todo
    public void asignarEnemigo(int pelea) {
        jugador2 = getOponentes().get(pelea);
        jugador1.miTurno = true;
        jugador2.miTurno = false;
    }

    public void asignarEnemigXrondas(int escenarios) {
        oponentes = new ArrayList();
        ArrayList<Caballero> cabaux;
        if (jugador1.getEstado() == 1) {
            cabaux = XML.CabMalos;
        } else {
            cabaux = XML.CabBuenos;
        }
        int rand;
        for (int i = 0; i < escenarios - 1; i++) {
            rand = random(cabaux.size(), 1);
            getOponentes().add(cabaux.get(rand));
            cabaux.remove(rand);
        }
        getOponentes().add(XML.CabMalos.get(0));
    }

    public void cerrarAplicacion() {
        System.exit(0);
    }

    //Despues que el usuario selecciona el caballero, se procedera a inicializar los jugadores
    public void initPlayers(Caballero player1, Caballero player2) {
        jugador1 = player1;
        jugador2 = player2;
    }

    // Funcion random que devuelve un random entre MaxRange y MinRange
    // Ejemplo max = 100 y min = 1 , esto significa un rango entre 100 ~ 1
    public int random(int maxRange, int minRange) {
        return (randomGenerator.nextInt(maxRange - minRange) + minRange);
    }

    public boolean puedoAtacar(Caballero jugador) {

        int resultado = random(100, 1);

        // Sea jugador.getProbaAtacar = 70 % 
        // if  resultado < 70 , return true
        // else resultado > 70 , return false

        //return (resultado > jugador.getProbaAtacar()) ? false : true ;
        return (resultado > jugador.getProbaAtacar()) ? false : true;
    }

    private boolean puedoUsarMiAtaque(Caballero jugador) {

        int resultado = random(100, 1);

        // Sea jugador.getProbaAtacar = 70 % 
        // if  resultado < 70 , return true
        // else resultado > 70 , return false

        return (resultado > jugador.getAtaqueActual().getProba()) ? false : true;
    }

    public void atacarEnemigo(Caballero jugador1, Caballero jugador2) {
        int damage;
        if (jugador1.isAtaqueEspecial()) {
            damage = (int) (jugador1.getAtaqueActual().getDano() * jugador1.getVida()) - jugador2.getDefensaActual().getResistencia();
        } else {
            damage = (int) jugador1.getAtaqueActual().getDano() - jugador2.getDefensaActual().getResistencia();
        }
        this.dano = damage;
        if (damage > 0) {
            String message = jugador1.getNombre() + " ataca con " + jugador1.getAtaqueActual().getNombre() + "(" + jugador1.getAtaqueActual().getDano() + ")" + "\n"
                    + jugador2.getNombre() + " se defiende con " + jugador2.getDefensaActual().getNombre() + "(" + jugador2.getDefensaActual().getResistencia() + ")" + "\n"
                    + jugador2.getNombre() + " recibe " + damage + " de ataque";
            setChanged();
            notifyObservers(message);
        } else {
            String message = jugador1.getNombre() + " ataca con " + jugador1.getAtaqueActual().getNombre() + "(" + jugador1.getAtaqueActual().getDano() + ")" + "\n"
                    + jugador2.getNombre() + " se defiende con " + jugador2.getDefensaActual().getNombre() + "(" + jugador2.getDefensaActual().getResistencia() + ")" + "\n"
                    + jugador2.getNombre() + " recupera " + damage * -1 + " de vida";
            setChanged();
            notifyObservers(message);
        }
        switch (jugador1.getAtaqueActual().getTipo()) {
            case 0: //normal
                jugador2.setVida(jugador2.getVida() - damage);
                break;
            case 1://sube la vida
                jugador1.aumentaVida(jugador1.getAtaqueActual().getHabilidad());
                JOptionPane.showMessageDialog(null, "EFECTO ESPECIAL: Aumenta vida");
                break;
            case 2://pierde turnos
                jugador2.setVida(jugador2.getVida() - damage);
                if (jugador1.getAtaqueActual().getHabilidad() != 0) {
                    setHabi(true);
                    JOptionPane.showMessageDialog(null, "EFECTO ESPECIAL: Pierde un Turno");
                }
                break;
        }
        switch (jugador2.getDefensaActual().getTipo()) {
            case 1://sube la vida
                jugador2.aumentaVida(jugador2.getAtaqueActual().getHabilidad());
                JOptionPane.showMessageDialog(null, "EFECTO ESPECIAL: Aumenta vida");
                break;
            case 2://pierde turnos
                if (jugador2.getAtaqueActual().getHabilidad() != 0) {
                    setHabi(true);
                    JOptionPane.showMessageDialog(null, "EFECTO ESPECIAL: Pierde Turno");
                }
                break;
            case 3://contraataque
                jugador1.setVida(jugador1.getVida() - jugador2.getDefensaActual().getHabilidad());
                JOptionPane.showMessageDialog(null, "EFECTO ESPECIAL: ContraAtaque directo");
                break;
        }

    }

    public void Pelear() {
        if (jugador1.miTurno) {
            if (puedoAtacar(jugador1)) {
                atacarEnemigo(jugador1, jugador2);
            }
            if (isHabi()) {
            } else {
                jugador1.miTurno = false;
                jugador2.miTurno = true;
            }

        } else {
            if (puedoAtacar(jugador2)) {
                atacarEnemigo(jugador2, jugador1);
            }
            if (isHabi()) {
            } else {
                jugador2.miTurno = false;
                jugador1.miTurno = true;
            }

        }

    }

    //este metodo es para probar.
    public boolean combate() {
        if (jugador2.getVida() <= 0) {
            return true;
        } else if (jugador1.getVida() <= 0) {
            return false;
        }
        if (jugador1.miTurno) {
            if (puedoAtacar(jugador1)) {
                if (puedoUsarMiAtaque(jugador1)) {
                    atacarEnemigo(jugador1, jugador2);
                }
            }
            jugador1.miTurno = false;
            jugador2.miTurno = true;
            return (combate());
        } else {
            if (puedoAtacar(jugador2)) {
                if (puedoUsarMiAtaque(jugador2)) {
                    atacarEnemigo(jugador2, jugador1);
                }
            }
            jugador2.miTurno = false;
            jugador1.miTurno = true;
            return (combate());
        }

    }

    private Caballero inicializarCaballeroActual(String nombreCaballero) {
        Caballero b = this.buscarCaballero(nombreCaballero);
        return b;
    }

    public void initGame(String nombreCaballero) {

        this.jugador1 = inicializarCaballeroActual(nombreCaballero);
        this.asignarEnemigXrondas(escenarios);
        this.asignarEnemigo(getPelea());
        try {
            try {
                sonido.close();
                sonido.open(AudioSystem.getAudioInputStream(getClass().getResource("/Aplicacion/pelea.wav")));
                sonido.start();
            } catch (LineUnavailableException ex) {
                //Logger.getLogger(Controlador.class.getName()).log(Level.SEVERE, null, ex);
            }
        } catch (UnsupportedAudioFileException | IOException ex) {
            //Logger.getLogger(Controlador.class.getName()).log(Level.SEVERE, null, ex);
        }
        Fight_Frame f = new Fight_Frame(this);
        f.create();

    }

    public void establishNextScenary() {
        if (this.escenarios > 1) {
            this.escenarios--;
            pelea++;
            this.asignarEnemigo(getPelea());
            this.jugador1.aumentaVida(200);
            try {
                try {
                    sonido.close();
                    sonido.open(AudioSystem.getAudioInputStream(new File("build/classes/Aplicacion/pelea.wav")));
                    sonido.start();
                } catch (LineUnavailableException ex) {
                    //Logger.getLogger(Controlador.class.getName()).log(Level.SEVERE, null, ex);
                }
            } catch (UnsupportedAudioFileException | IOException ex) {
                //Logger.getLogger(Controlador.class.getName()).log(Level.SEVERE, null, ex);
            }
            Fight_Frame f = new Fight_Frame(this);
            f.create();
        }else{
            sonido.close();
            JOptionPane.showMessageDialog(null, "Muchas felicidades, sobreviste a \ntodas las batallas.");
                Main_Frame principal =
                        new Main_Frame(new Controlador());
                principal.init();
        }
    }

    /**
     * @return the oponentes
     */
    public ArrayList<Caballero> getOponentes() {
        return oponentes;
    }

    public int Numpelea() {
        return this.getPelea() + 1;
    }

    /**
     * @return the pelea
     */
    public int getPelea() {
        return pelea;
    }

    /**
     * @return the habi
     */
    public boolean isHabi() {
        return habi;
    }

    /**
     * @param habi the habi to set
     */
    public void setHabi(boolean habi) {
        this.habi = habi;
    }
}
